﻿using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MainForm
{
    public partial class Countdown : Panel
    {
        public Countdown()
        {
            this.Init();
            InitializeComponent();
        }
        private long _counterFreq = 0;
        protected long ContextTime { get; set; }
        protected Factory D2D1Factory { get; set; }
        protected WindowRenderTarget D2D1RenderTarget { get; set; }
        protected SolidColorBrush BarBrush { get; set; }
        protected SolidColorBrush BackBrush { get; set; }
        protected SolidColorBrush TextBrush { get; set; }
        protected SharpDX.DirectWrite.Factory FontFactory { get; set; }
        protected SharpDX.DirectWrite.TextFormat NumberTextFormat { get; set; }
        protected SharpDX.DirectWrite.TextFormat NameTextFormat { get; set; }
        public List<GameEvent> EventList { get; set; }
        [Browsable(true)]
        public int BarHeight { get; set; }
        [Browsable(true)]
        public System.Drawing.Color CountdownBackColor { get; set; }
        [Browsable(true)]
        public System.Drawing.Color CountdownBarColor { get; set; }
        [Browsable(true)]
        public System.Drawing.Color CountdownTextColor { get; set; }
        private CancellationTokenSource Stop { get; set; }

        public event EventHandler End;

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
        }
        protected override void OnResize(EventArgs eventargs)
        {
            this.D2D1RenderTarget.Resize(new SharpDX.Size2(this.ClientSize.Width, this.ClientSize.Height));
            base.OnResize(eventargs);
        }
        public void Init()
        {
            this.BarHeight = 30;
            this.Stop = new CancellationTokenSource();
            Factory factory = new Factory(FactoryType.MultiThreaded);
            RenderTargetProperties renderProps = new RenderTargetProperties
            {
                PixelFormat = new PixelFormat(),
                Usage = RenderTargetUsage.None,
                Type = RenderTargetType.Default
            };
            // 渲染目标属性。
            HwndRenderTargetProperties hwndProps = new HwndRenderTargetProperties()
            {
                // 承载控件的句柄。
                Hwnd = this.Handle,
                // 控件的尺寸。
                PixelSize = new SharpDX.Size2(this.ClientSize.Width, this.ClientSize.Height),
                PresentOptions = PresentOptions.None
            };
            // 渲染目标。
            this.D2D1RenderTarget = new WindowRenderTarget(factory, renderProps, hwndProps)
            {
                AntialiasMode = AntialiasMode.PerPrimitive
            };
            this.BarBrush = new SolidColorBrush(this.D2D1RenderTarget, this.CreateColor4(this.CountdownBarColor));

            this.BackBrush = new SolidColorBrush(this.D2D1RenderTarget, this.CreateColor4(this.CountdownBackColor));
            this.TextBrush = new SolidColorBrush(this.D2D1RenderTarget, this.CreateColor4(this.CountdownTextColor));

            this.FontFactory = new SharpDX.DirectWrite.Factory(SharpDX.DirectWrite.FactoryType.Shared);
            this.NumberTextFormat = new SharpDX.DirectWrite.TextFormat(this.FontFactory, "Consolas", SharpDX.DirectWrite.FontWeight.Bold, SharpDX.DirectWrite.FontStyle.Normal, 12f);
            this.NameTextFormat = new SharpDX.DirectWrite.TextFormat(this.FontFactory, "微软雅黑", SharpDX.DirectWrite.FontWeight.Bold, SharpDX.DirectWrite.FontStyle.Normal, 12f);

            this.D2D1Factory = factory;

            Win32API.QueryPerformanceFrequency(ref this._counterFreq);
        }

        public void Start(List<GameEvent> eventList)
        {
            this.EventList = eventList;
            this.StartTime();
            Task.Run(() =>
            {
                long o1, o2;
                var brush = new SolidColorBrush(this.D2D1RenderTarget, new RawColor4(0, 0, 0, 1));
                while (this.ContextTime >= 0)
                {
                    var needHandleEvents = this.EventList.Where(g => g.TimeMilliSecond * 10000 - this.ContextTime <= 30 * 1000 * 10000 && !g.Handled).Take(3).ToList();
                    for (var i = 0; i < needHandleEvents.Count; i++)
                    {
                        var gameEvent = needHandleEvents[i];
                        if (gameEvent.TextSize.IsEmpty)
                        {
                            gameEvent.TextSize = this.MeasureString(this.FontFactory, this.NameTextFormat, gameEvent.EventName);
                        }
                        if (gameEvent.TimeMilliSecond * 10000 < this.ContextTime)
                        {
                            gameEvent.Handled = true;
                            this.D2D1RenderTarget.BeginDraw();
                            this.D2D1RenderTarget.FillRectangle(new RawRectangleF(0, 0, this.Width, this.Height), brush);
                            this.D2D1RenderTarget.TryEndDraw(out o1, out o2);
                            break;
                        }
                        var nowLeft = ((gameEvent.TimeMilliSecond * 10000f - this.ContextTime) / (Math.Min(30 * 1000, gameEvent.TimeMilliSecond) * 10000f) * this.ClientSize.Width);
                        var rect = new RawRectangleF(nowLeft, i * (this.BarHeight + 5), this.Width, this.BarHeight + i * (this.BarHeight + 5));
                        this.D2D1RenderTarget.BeginDraw();
                        this.D2D1RenderTarget.FillRectangle(rect, this.BarBrush);
                        rect = new RawRectangleF(0, i * (this.BarHeight + 5), nowLeft, this.BarHeight + i * (this.BarHeight + 5));
                        this.D2D1RenderTarget.FillRectangle(rect, this.BackBrush);
                        var textLeft = (this.ClientSize.Width - gameEvent.TextSize.Width) / 2;
                        var textHeight = i * (this.BarHeight + 5) + (this.BarHeight - gameEvent.TextSize.Height) / 2;
                        rect = new RawRectangleF(textLeft, textHeight, this.Width, textHeight);
                        this.D2D1RenderTarget.DrawText(
                            gameEvent.EventName,
                            this.NameTextFormat,
                            rect,
                            this.TextBrush);
                        rect = new RawRectangleF(this.ClientSize.Width - 20, textHeight, this.ClientSize.Width, textHeight);
                        this.D2D1RenderTarget.DrawText(
                        string.Format("{0}S", Math.Ceiling((gameEvent.TimeMilliSecond * 10000f - this.ContextTime) / 1000 / 10000f)),
                        this.NumberTextFormat,
                        rect,
                        this.TextBrush
                        );
                        this.D2D1RenderTarget.TryEndDraw(out o1, out o2);
                    }
                }
                brush.Dispose();
                if (!this.Stop.IsCancellationRequested)
                {
                    this.End?.Invoke(this, null);
                }
            }, this.Stop.Token);
        }
        private void StartTime()
        {
            var endTime = this.EventList.Max(g => g.TimeMilliSecond);
            this.ContextTime = 0;
            var stTimeTicks = DateTime.Now.Ticks;
            Task.Run(() =>
            {
                while (this.ContextTime < endTime * 10000 && !Stop.IsCancellationRequested)
                {
                    this.ContextTime = DateTime.Now.Ticks - stTimeTicks;
                }
                this.ContextTime = -1;
            }, this.Stop.Token);
        }

        private RawColor4 CreateColor4(byte r, byte g, byte b)
        {
            return new RawColor4(r / 255f, g / 255f, b / 255f, 1);
        }
        private RawColor4 CreateColor4(System.Drawing.Color color)
        {
            return new RawColor4(color.R / 255f, color.G / 255f, color.B / 255f, 1);
        }
        private System.Drawing.SizeF MeasureString(SharpDX.DirectWrite.Factory factory, SharpDX.DirectWrite.TextFormat format, string text)
        {
            using (var layout = new SharpDX.DirectWrite.TextLayout(factory, text, format, this.ClientSize.Width, this.ClientSize.Height))
            {
                return new System.Drawing.SizeF(layout.Metrics.Width, layout.Metrics.Height);
            }
        }
        public void StopAll()
        {
            this.Stop.Cancel();
        }
        public void ChangeColor(System.Drawing.Color backColor, System.Drawing.Color barColor, System.Drawing.Color textColor)
        {
            this.BackBrush = new SolidColorBrush(this.D2D1RenderTarget, this.CreateColor4(backColor));
            this.BarBrush = new SolidColorBrush(this.D2D1RenderTarget, this.CreateColor4(barColor));
            this.TextBrush = new SolidColorBrush(this.D2D1RenderTarget, this.CreateColor4(textColor));
        }
        ~Countdown()
        {
            this.D2D1Factory.Dispose();
            this.D2D1RenderTarget.Dispose();
            this.BarBrush.Dispose();
            this.BackBrush.Dispose();
            this.TextBrush.Dispose();
            this.FontFactory.Dispose();
            this.NumberTextFormat.Dispose();
            this.NameTextFormat.Dispose();
            Console.WriteLine("dispose!");
        }
    }
}
